import mc
import version
from sage import LOG

'''A python wrapper to manage local application configuration.

This module provides a wrapper around local configuration operations.

In addition, the module defines a MediaPathMapper class that performs
conversions of media paths from the SageTV local version to a SMB path
visible to the Boxee device.
'''


'''Define a global media mapper object.

This object will be updated automatically when MediaPathMaps is changed, which
eliminates the overhead of creating the MediaPathMapper multiple times.
'''
mediaPathMapper = None


def initialize():
	'''Initializes the configuration wrapper.'''
	cfg_ver = _getValue('AppVersion')
	app_ver = version.ver
	
	# Perform required initialization
	refreshMediaPathMapper()
		
	# Store the current app version
	_setValue('AppVersion', app_ver)

def resetAll():
	'''Resets all values stored in configuration.'''
	mc.GetApp().GetLocalConfig().ResetAll()

def _getValue(k):
	'''Retrieves a value from the application configuration.

		     k: A string representing the configuration item key
		return: A string containing the configuration item value
	'''
	return mc.GetApp().GetLocalConfig().GetValue(k)
	
def _setValue(k, v):
	'''Stores a value into the application configuration.

		     k: A string representing the configuration item key
			 v: A string containing the configuration item value
		return: None
	'''
	mc.GetApp().GetLocalConfig().SetValue(k, v)
	
	
#
# Configuration Parameters
#

def getSageServer():
	'''Retrieves the SageTV server information.
			Format: "server:port"
	'''
	return _getValue('SageServer')
	
def setSageServer(value):
	'''Stores the SageTV server information.
			Format: "server:port"
	'''
	_setValue('SageServer', value)
	
	
def getJettyUser():
	'''Retrieves the username used to connect to the SageTV Jetty server.'''
	return _getValue('JettyUser')
	
def setJettyUser(value):
	'''Stores the username used to connect to the SageTV Jetty server.'''
	_setValue('JettyUser', value)
	
	
def getJettyPassword():
	'''Retrieves the password used to connect to the SageTV Jetty server.'''
	return _getValue('JettyPassword')
	
def setJettyPassword(value):
	'''Stores the password used to connect to the SageTV Jetty server.'''
	_setValue('JettyPassword', value)
	
	
def getMediaPathMaps():
	'''Retrieves a delimtied list of media path maps. 
			Format: "localPathA,remotePathA;localPathB,remotePathB;..."
	'''
	return _getValue('MediaPathMaps')
	
def setMediaPathMaps(value):
	'''Stores a delimtied list of media path maps. 
			Format: "localPathA,remotePathA;localPathB,remotePathB;..."
	'''
	_setValue('MediaPathMaps', value)
	refreshMediaPathMapper()

	
	
	
#
# MediaPathMapper Support
#
	
def refreshMediaPathMapper():
	'''Updates the global MediaPathMapper object to reflect changes in configuration.'''
	global mediaPathMapper
	mediaPathMapper = MediaPathMapper(getMediaPathMaps())
	
class MediaPathMapper:
	'''Converts media file paths from SageTV local path to network accessible path.'''
	PROP_PART_DELIM = ','
	PROP_MAP_DELIM  = ';'
	
	def __init__(self, init_str, network_prefix = r'smb://'):
		'''Constructor.

			      init_str: The initialization string containing a delimited list of media path maps.
			network_prefix: The prefix that will be appended to network paths.
		'''
		self._sorted_keys = []
		self._map_dict = {}
		self.network_prefix = network_prefix
		
		# Populate the map dictionary from the init string
		for mp in init_str.split(self.PROP_MAP_DELIM):
			map_parts = mp.split(self.PROP_PART_DELIM)
			if len(map_parts) == 2:
				self.addMap(map_parts[0], map_parts[1])
			
	def _updateKeys(self):
		'''Updates the sorted keys list so it stays in sync with the map dictionary. Keys are sorted in reverse order by length.'''
		del self._sorted_keys[:]
		self._sorted_keys = self._map_dict.keys()
		self._sorted_keys.sort(key = len)
		self._sorted_keys.reverse()
	
	def _preparePath(self, path, striplead):
		'''Prepares a path string for internal usage.

			     path: A string containing the path to prepare
			striplead: A boolean value indicating if leading slashes should be stripped
			   return: A string containing the converted path, or the original path if no conversion was necessary
		'''
		path = path.replace('\\', '/')
		path = path.strip()
		path = path.rstrip('/')
		if striplead:
			path = path.lstrip('/')
		return path
		
	def _convertPathInternal(self, map_key, path):
		'''Converts a path value using the specified map.

		map_key: A string indicating the map to use for the conversion
		   path: A string containing the path to convert
		 return: A string containing the converted path.
		'''
		origpath = path
		if map_key is None:
			path = path.lstrip('/')
		else:
			path = self._map_dict[map_key] + path[len(map_key):]
		path = self.network_prefix + path
		LOG.LogDebug('MediaPathMapper: Converted path "%s" to "%s"' % (origpath, path))
		return path
		
	def addMap(self, local_prefix, remote_prefix):
		'''Adds a map entry into the dictionary.

			 local_prefix: A string containing the local path prefix (ie "C:\SageTV")
			remote_prefix: A string containing the remote path prefix (ie "//mediaserver/TV")
				   return: None
		'''
		# Prepare the prefix values
		local_prefix = self._preparePath(local_prefix, True)
		remote_prefix = self._preparePath(remote_prefix, True)
		
		# Add the value to the dictionary and update the sorted keys
		LOG.LogDebug('MediaPathMapper: Adding map "%s" -> "%s"...' % (local_prefix, remote_prefix))
		self._map_dict[local_prefix] = remote_prefix
		self._updateKeys()
		
		
	def removeMap(self, local_prefix):
		'''Removes a map entry from the dictionary.
		
			local_prefix: A string containing the local path prefix (ie "C:\SageTV")
				  return: None
		'''
		LOG.LogDebug('MediaPathMapper: Removing map "%s"...' % local_prefix)
		del self._map_dict[local_prefix]
		self._updateKeys()
		
	def clearMaps(self):
		'''Clears all entries from the map dictionary.'''
		LOG.LogDebug('MediaPathMapper: Clearing maps...')
		self._map_dict.clear()
		self._updateKeys()
		
	def maps(self):
		'''Returns the list of map entries as key-value-pairs.'''
		return self._map_dict.items
		
	def implodeMaps(self):
		'''Retrieves a delimtied list of the map entries. 
				Format: "localPathA,remotePathA;localPathB,remotePathB;..."
		'''
		return self.PROP_MAP_DELIM.join(["%s%s%s" % (k, self.PROP_PART_DELIM, v) for k, v in self._map_dict.items()])
		
	def convertPath(self, path):
		'''Converts a media file path using the closest matching map entry.
		
			The conversion algorithm first performs a case-sensitive search for the closest matching map value. If
			that fails, a case-insensitive search is performed.  If both searches fail, the method determines if the
			path is a network path, and if so, appends the network prefix to the path before returning it.
		
			path: A string containing the media file path to convert
			return: A string containing the converted path, or the original path if no conversion was necessary
		'''
		origpath = path
		path = self._preparePath(path, False)
		
		# Search for case-sensitive match first
		for k in self._sorted_keys:
			if path.startswith(k):
				return self._convertPathInternal(k, path)
				
		# Search for case-insensitive match
		lower_path = path.lower()
		for k in self._sorted_keys:
			if lower_path.startswith(k.lower()):
				return self._convertPathInternal(k, path)
				
		# No match was found, check if the path is a network path
		if path.startswith('//'):
			return self._convertPathInternal(None, path)
			
		# Return the original path
		LOG.LogDebug('MediaPathMapper: Cannot convert path "%s"' % path)
		return origpath